﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Elsa.Expressions.Contracts;
using Elsa.Extensions;
using Elsa.JavaScript.Models;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Elsa.Workflows.UIHints;
using Microsoft.Extensions.Hosting;
using T.FAS.Runtime.MF.Activities.Extensions;
using T.FAS.Runtime.MF.Activities.Services;
using T.FAS.Runtime.MF.Entity;
using T.FAS.Runtime.MF.SPI;
using YamlDotNet.Core;
using YamlDotNet.Core.Tokens;
using static System.Net.Mime.MediaTypeNames;

namespace T.FAS.Runtime.MF.Activities
{
    public class LLMActivity : FASCodeActivity<object?>
    {
        /// <summary>
        /// 选择的大模型
        /// </summary>
        [Input(Description = "备选模型", Options = new[] { "qwen-max" }, DefaultValue = "qwen-max", UIHint = InputUIHints.DropDown, Order = 1)]
        public string LLMModel { get; set; }
        /// <summary>
        /// 模型参数
        /// </summary>
        [Input(Description = "模型参数", UIHint = "switch-editor", Order = 2)]
        public List<BranchCondition> LLMOptions { get; set; }
        /// <summary>
        /// 上下文
        /// </summary>
        [Input(Description = "上下文", UIHint = InputUIHints.MultiText, Order = 3)]
        public string Context { get; set; }
        /// <summary>
        /// 提示词列表
        /// </summary>
        [Input(Description = "提示词列表", UIHint = "switch-editor", Order = 4)]
        public List<BranchCondition> Prompts { get; set; }


        /// <summary>
        /// 提示词列表
        /// </summary>
        [Input(Description = "提示词变量列表", UIHint = "switch-editor", Order = 5)]
        public List<BranchCondition> PromptVariables { get; set; }

        /// <summary>
        /// 最大重试次数
        /// </summary>
        [Input(Description = "最大重试次数",UIHint = InputUIHints.SingleLine, Order = 6)]
        public int MaxRetryCont { get; set; } = 3;

        /// <summary>
        /// 最大重试次数
        /// </summary>
        [Input(Description = "重试时间间隔", UIHint = InputUIHints.SingleLine, Order = 7)]
        public int DelaySeconds { get; set; } = 10;

        /// <summary>
        /// 节点默认输出的变量
        /// </summary>
        public List<Variable> OutputVariables { get; set; }

        protected override async void Execute(ActivityExecutionContext context)
        {
            try
            {
                var factory = context.GetService<Func<string, ILLMModelRequestService>>();
                var service = factory(LLMModel);
                var outputInstanceValues = new Dictionary<string, object>();

               await  OperationReTry.TryOperation(async  () =>
                {
                    var requestParam = new RequestParam
                    {
                        ModelName = LLMModel,
                        Context = await GetContext(context),
                        Options = LLMOptions.ToDictionary(t => t.Label, t => t.Condition.Value)!,
                        Prompts = await BuildPromptsDictionary(context),
                    };
                    SetInputInstanceValues(requestParam,context.Id);
                    var result = await service.Request(requestParam).ConfigureAwait(false); 
                    if (result != null)
                    {
                        context.SetResult(result);

                        //此处为输出变量赋值，实际上只有一个返回变量record
                        if (OutputVariables != null)
                        {
                            foreach (var item in OutputVariables)
                            {
                                var varName = item.Name.Replace(Id.Replace("-", ""),"").Replace("Variable", "");
                                if (varName.ToLower()=="text")
                                {
                                    context.SetVariable(item.Name, result.Response);

                                    //向迭代返回值数组中赋值,这里会累计每次迭代的执行结果，返回到数组中
                                    context.SetIterationVariableValue(item.Name, result.Response);

                                    //给输出参数赋值
                                    outputInstanceValues["text"] = result.Response;
                                }
                                else if (varName.ToLower() == "usage")
                                {
                                    context.SetVariable(item.Name, result.TokenUsage);

                                    //向迭代返回值数组中赋值,这里会累计每次迭代的执行结果，返回到数组中
                                    context.SetIterationVariableValue(item.Name, result.TokenUsage);

                                    //给输出参数赋值
                                    outputInstanceValues["usage"] = result.TokenUsage;
                                }
                            }
                        }
                        this.OutputInstanceValues[context.Id] = outputInstanceValues;
                    }
                    return Task.FromResult(true);
                }, MaxRetryCont, DelaySeconds).ConfigureAwait(false);

               
            }
            catch (Exception ex)
            {
                await context.FaultAndComplete(ex, this);
            }
        }

        private void SetInputInstanceValues(RequestParam requestParam,string contextId)
        {
            var inputInstanceValues = new Dictionary<string, object>();
            inputInstanceValues["model"] = requestParam.ModelName;
            inputInstanceValues["context"] = requestParam.Context;
            inputInstanceValues["options"] = requestParam.Options;
            inputInstanceValues["prompt"] = requestParam.Prompts.Select(t => new  { role = t.Key.Split("/").Length > 1 ? t.Key.Split("/")[1] : t.Key.Split("/")[0], content = t.Value.ToString() }).ToList();
            this.InputInstanceValues[contextId] = inputInstanceValues;
        }

        public async Task<Dictionary<string, string>> BuildPromptsDictionary(ActivityExecutionContext context)
        {
            var promptsDict = new Dictionary<string, string>();
            var PromptVariableValues = new Dictionary<string, string>();
            var expressionEvaluator = context.GetRequiredService<IExpressionEvaluator>();

            //将提示词变量中的替换为实际运行的值
            foreach (var promptVariale in PromptVariables)
            {
                var result = await expressionEvaluator.EvaluateAsync<string?>(promptVariale.Condition, context.ExpressionExecutionContext);
                PromptVariableValues[promptVariale.Label] = result;
                promptVariale.Condition = Elsa.Expressions.Models.Expression.LiteralExpression(result);
            }
            foreach (var prompt in Prompts)
            {
                var value = await GetPrompt(context, prompt.Condition.Value.ToString(), PromptVariableValues);
                promptsDict[prompt.Label] = value;
                prompt.Condition = Elsa.Expressions.Models.Expression.LiteralExpression(value);
            }

            return promptsDict;
        }
        /// <summary>
        /// 将提示词中的变量转换为运行时的值，并替换掉
        /// </summary>
        /// <param name="prompt"></param>
        /// <returns></returns>
        private async Task<string> GetPrompt(ActivityExecutionContext context, string prompt, Dictionary<string, string> PromptVariableValues)
        {
            foreach (var varItem in PromptVariableValues)
            {
                if (varItem.Value == null) continue;

                prompt = prompt.Replace(varItem.Key, varItem.Value);
               
            }
            return prompt;
        }

   
        private async Task<string> GetContext(ActivityExecutionContext context)
        {
            return await GetVarValue(context,Context);
        }

        private async Task<string> GetVarValue(ActivityExecutionContext context,string varName)
        {
            if (string.IsNullOrEmpty(varName)) return string.Empty;

            var varItem = varName.Split('.')[0];
            var sbScript = new StringBuilder();

            sbScript.AppendLine($"let {varItem} = JSON.parse(variables.{varItem});");
            sbScript.AppendLine($" {varName}; ");

            var expressionEvaluator = context.GetRequiredService<IExpressionEvaluator>();
            var result = await expressionEvaluator.EvaluateAsync<string?>(JavaScriptExpression.Create(sbScript.ToString()), context.ExpressionExecutionContext);
            return result;
        }

    }
}
